/* 
 * Copyright (C) 2011 rccloud.com All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.rccloud.inproxy;

import java.util.ArrayList;
import java.util.List;

/**
 * The main class for converting between main object and proxy object
 */
public class Inproxy {

	/**
	 * Initiate proxy factory, must invoke before any convert method.
	 */
	public static void init(InproxyConfig config) {
		InproxyFactory.getFactory(config);
	}

	/**
	 * Convert from main object to proxy object
	 * 
	 * @param <T>
	 *            Proxy class type
	 * @param proxyClass
	 *            Proxy class
	 * @param mainObject
	 *            Main object
	 * @return Return proxy object
	 */
	@SuppressWarnings("unchecked")
	public static <T> T to(Class<T> proxyClass, Object mainObject) {
		if (mainObject == null)
			return null;

		// Get proxy info from factory
		InproxyInfo info = InproxyFactory.getFactory()
				.getProxyInfoFromMainClass(mainObject.getClass(), proxyClass);
		if (info == null) {
			throw new ProxyConverterException(
					"Can't find proxy mapping info for main class["
							+ mainObject.getClass() + "]");
		}

		try {

			T proxy = (T) info.getProxyClass().newInstance();
			List<MethodProxyInfo> setMethodList = info.getMethodList();

			for (MethodProxyInfo setMethodInfo : setMethodList) {

				// Set value
				Object value = setMethodInfo.getTargetGetMethod().invoke(
						mainObject);
				Object newvalue = setMethodInfo
						.getConverter()
						.newInstance()
						.convertToProxy(
								setMethodInfo.getProxyField().getType(), value);
				setMethodInfo.getSetMethod().invoke(proxy, newvalue);
			}

			return proxy;

		} catch (InstantiationException ex) {
			throw new ProxyConverterException(
					"Can't instance proxy object class, it must have a public default constructor.");
		} catch (IllegalAccessException ex) {
			throw new ProxyConverterException(
					"Can't instance proxy object class, it must have a public default constructor.");
		} catch (ProxyConverterException ex) {
			throw ex;
		} catch (Exception ex) {
			ex.printStackTrace();
			throw new ProxyConverterException(
					"Can't set value, please check get/set method in main object class and proxy object class. Cause by ["
							+ ex + "].");
		}
	}

	/**
	 * Convert from main object to proxy object in list
	 * 
	 * @param <T>
	 *            Proxy class type
	 * @param proxyClass
	 *            Proxy class type
	 * @param mainObjects
	 *            Main objects
	 * @return Proxy objects
	 */
	public static <T> List<T> toList(Class<T> proxyClass, List<?> mainObjects) {
		if (mainObjects == null)
			return null;
		List<T> ts = new ArrayList<T>();
		for (Object mainObject : mainObjects)
			if (mainObject != null)
				ts.add(to(proxyClass, mainObject));
		return ts;
	}

	/**
	 * Convert from proxy object to main object
	 * 
	 * @param proxyObject
	 *            Proxy object
	 * @return Return main object
	 */
	public static Object from(Object proxyObject) {
		if (proxyObject == null)
			return null;

		// Get proxy info from factory
		InproxyInfo info = InproxyFactory.getFactory()
				.getProxyInfoFromProxyClass(proxyObject.getClass());
		if (info == null) {
			throw new ProxyConverterException(
					"Can't find proxy mapping info for proxy class["
							+ proxyObject.getClass() + "]");
		}

		try {
			Object mainObject = info.getMainClass().newInstance();
			List<MethodProxyInfo> setMethodList = info.getMethodList();
			for (MethodProxyInfo setMethodInfo : setMethodList) {

				// Set value
				Object value = setMethodInfo.getGetMethod().invoke(proxyObject);
				Object newvalue = setMethodInfo.getConverter().newInstance()
						.convertFromProxy(value);
				setMethodInfo.getTargetSetMethod().invoke(mainObject, newvalue);
			}

			return mainObject;
		} catch (InstantiationException ex) {
			throw new ProxyConverterException(
					"Can't instance main object class, it must have a public default constructor.");
		} catch (IllegalAccessException ex) {
			throw new ProxyConverterException(
					"Can't instance main object class, it must have a public default constructor.");
		} catch (Exception ex) {
			throw new ProxyConverterException(
					"Can't set value, please check get/set method in main object class and proxy object class.");
		}
	}

	/**
	 * Convert from proxy object to main object
	 * 
	 * @param <T>
	 *            Main object class type
	 * @param mainClass
	 *            Main object class
	 * @param proxyObjects
	 *            Proxy objects
	 * @return Main objects
	 */
	@SuppressWarnings("unchecked")
	public static <T> List<T> fromList(Class<T> mainClass, List<?> proxyObjects) {
		return (List<T>) fromList(proxyObjects);
	}

	/**
	 * Convert from proxy object to main object
	 * 
	 * @param proxyObjects
	 *            Proxy objects
	 * @return Main objects
	 */
	public static List<?> fromList(List<?> proxyObjects) {
		if (proxyObjects == null)
			return null;
		List<Object> result = new ArrayList<Object>();
		for (Object proxyObject : proxyObjects)
			if (proxyObject != null)
				result.add(from(proxyObject));
		return result;
	}
}
